use crate::config::{self, RegistryServiceEntry};
use core::str;
use std::{borrow::Cow, collections::HashMap, process::Command};

// 广播
pub fn run_cmd(
    srv_name: &str,
    mut cmd: Vec<String>,
    cwd: Option<String>,
    common_env: Option<HashMap<String, String>>,
    env: Option<HashMap<String, String>>,
) -> Option<(String, String)> {
    let args = cmd.split_off(1);
    let prog = cmd[0].clone();
    let mut cmd = Command::new(prog);
    cmd.env("AYISHA_SERVICE_NAME", srv_name);

    if let Some(cwd) = cwd {
        cmd.current_dir(&cwd);
        cmd.env("AYISHA_SERVICE_ROOT", cwd);
    }

    // 设置环境变量
    if let Some(ref vars) = common_env {
        cmd.envs(vars);
    }
    if let Some(ref vars) = env {
        cmd.envs(vars);
    }

    // DEBUG print start
    for (k, v) in cmd.get_envs() {
        let v = match v {
            Some(v) => v.to_string_lossy(),
            None => Cow::from(""),
        };
        log::debug!("+++ env: {}={}", k.to_string_lossy(), v);
    }
    let cwd = match cmd.get_current_dir() {
        Some(cwd) => format!("{}", cwd.display()),
        None => "".to_owned(),
    };
    log::debug!("+++ cwd: {}", cwd);
    // DEBUG print end
    match cmd.args(args.clone()).output() {
        Ok(output) => {
            log::debug!("+++ program: {}", cmd.get_program().to_string_lossy());
            let mut print_args = Vec::new();
            {
                print_args.push(format!("{:?}", cmd.get_program().to_string_lossy()));
                for arg in cmd.get_args() {
                    print_args.push(format!("{:?}", arg.to_string_lossy()));
                }
            }

            let stdout = decode(&output.stdout, 0);
            let stderr = decode(&output.stderr, 0);
            if output.status.success() {

                if stderr.len() > 0 {
                    log::error!(
                        "Command execute completed with errors, run: [{}], stderr: {:?}, stdout: {:?}",
                        print_args.join(", "),
                        stderr.trim_end_matches("\n"),
                        stdout.trim_end_matches("\n")
                    );
                } else {
                    log::info!(
                        "Command execute completed, run: [{}], stderr: {:?}, stdout: {:?}",
                        print_args.join(", "),
                        stderr.trim_end_matches("\n"),
                        stdout.trim_end_matches("\n")
                    );
                }
                return Some((stdout.to_owned(), stderr.to_owned()));
            }

            if stderr.len() > 0 {
                log::error!(
                    "Command execute completed with errors, stderr: {:?}, stdout: {:?}, code: {:?}",
                    stderr.trim_end_matches("\n"),
                    stdout.trim_end_matches("\n"),
                    output.status.code().unwrap_or_default()
                );
            } else {
                log::info!(
                    "Command execute completed with errors, stderr: {:?}, stdout: {:?}, code: {:?}",
                    stderr.trim_end_matches("\n"),
                    stdout.trim_end_matches("\n"),
                    output.status.code().unwrap_or_default()
                );
            }
            
        }
        Err(e) => {
            let mut print_args = Vec::new();
            {
                print_args.push(format!("{:?}", cmd.get_program().to_string_lossy()));
                for arg in cmd.get_args() {
                    print_args.push(format!("{:?}", arg.to_string_lossy()));
                }
            }
            log::error!(
                "Command failed to start, run: [{}], cause: {e}",
                print_args.join(" ")
            );
        }
    }
    None
}

// 执行检查脚本
pub fn run_check_cmd(srv_name: String, rse: RegistryServiceEntry) {
    let cloned_rse = rse.clone();
    {
        let (_stdout, stderr) = match run_cmd(
            &srv_name,
            rse.check_cmd,
            rse.root,
            config::get_registry_env(),
            rse.env,
        ) {
            Some((o, e)) => (o, e),
            None => return,
        };

        // log::info!(
        //     "Command execute completed{}, stderr: {:?}, stdout: {:?}",
        //     if stderr.len() > 0 { " with errors" } else { "" },
        //     stderr.trim_end_matches("\n"),
        //     stdout.trim_end_matches("\n")
        // );
        // log::info!(
        //     "Command execute completed with errors, stderr: {:?}, stdout: {:?}, Start executing command: {:?}",
        //     stderr.trim_end_matches("\n"),
        //     stdout.trim_end_matches("\n"),
        //     cloned_rse.start_cmd
        // );

        if stderr.len() == 0 {
            return;
        }

        log::info!("Start executing command, run: {:?}", rse.start_cmd);
    }

    // if let Some((stdout, stderr)) = {
    //     log::info!(
    //         "Command execute completed{}, stderr: {:?}, stdout: {:?}",
    //         if stderr.len() > 0 { " with errors" } else { "" },
    //         stderr.trim_end_matches("\n"),
    //         stdout.trim_end_matches("\n")
    //     );
    // }
    let _ = run_cmd(
        &srv_name,
        cloned_rse.start_cmd,
        cloned_rse.root,
        config::get_registry_env(),
        cloned_rse.env,
    );
}

pub fn run_to_leader_cmd(srv_name: &str, rse: RegistryServiceEntry) {
    let common_env = config::get_registry_env();
    let _ = run_cmd(srv_name, rse.to_leader_cmd, rse.root, common_env, rse.env);
}

pub fn run_to_follower_cmd(srv_name: &str, rse: RegistryServiceEntry) {
    let common_env = config::get_registry_env();
    let _ = run_cmd(srv_name, rse.to_follower_cmd, rse.root, common_env, rse.env);
}

pub fn run_stop_cmd(srv_name: &str, rse: RegistryServiceEntry) -> Option<(String, String)> {
    let common_env = config::get_registry_env();
    run_cmd(srv_name, rse.stop_cmd, rse.root, common_env, rse.env)
}

pub fn on_switch_to_leader() {
    for (srv_name, rse) in config::get_registry_services() {
        run_to_leader_cmd(&srv_name, rse);
    }
}

pub fn on_switch_to_follower() {
    for (srv_name, rse) in config::get_registry_services() {
        run_to_follower_cmd(&srv_name, rse);
    }
}

const CODE_PAGES: [&str; 35] = [
    "UTF-8",
    "gb18030",
    "GBK",
    "UTF-16LE",
    "UTF-16BE",
    "Shift_JIS",
    "EUC-KR",
    "Big5",
    "IBM866",
    "windows-874",
    "windows-1250",
    "windows-1251",
    "windows-1252",
    "windows-1253",
    "windows-1254",
    "windows-1255",
    "windows-1256",
    "windows-1257",
    "windows-1258",
    "macintosh",
    "x-mac-cyrillic",
    "KOI8-R",
    "EUC-JP",
    "KOI8-U",
    "ISO-8859-2",
    "ISO-8859-3",
    "ISO-8859-4",
    "ISO-8859-5",
    "ISO-8859-6",
    "ISO-8859-7",
    "ISO-8859-8",
    "ISO-8859-13",
    "ISO-8859-15",
    "ISO-8859-8-I",
    "ISO-2022-JP",
];
pub fn decode(bytes: &Vec<u8>, find_next: usize) -> String {
    let encoding = config::get_encoding(Some(CODE_PAGES[find_next].to_owned()));
    let (cow, _encoding_used, had_errors) = encoding.decode(bytes);
    if had_errors {
        // 国标
        return decode(bytes, find_next + 1);
    }

    cow.to_string()
}

#[cfg(test)]
mod tests {
    use crate::config::RegistryBroadcastEntry;

    use super::run_cmd;

    #[test]
    fn test() {
        let rbe = RegistryBroadcastEntry {
            root: None,
            cmd: vec![
                "cmd".to_owned(),
                "/C".to_owned(),
                "C:\\Users\\BK-liao\\Desktop\\abc.bat".to_owned(),
            ],
            interval_secs: None,
            input: None,
            output: None,
            props: Vec::new(),
        };

        if let Some((stdout, stderr)) = run_cmd("abc", rbe.cmd, None, None, None) {
            println!("stdout:{}", stdout);
            println!("stderr:{}", stderr);
        }
    }
}
//     use crate::config::{RegistryBroadcastEntry, RegistryServiceEntry};

//     use super::run_cmd;

//     #[test]
//     fn test() {
//         // C:\\Users\\BK-liao\\Desktop\\abc.bat:
//         // @echo off
//         // set AYISHA_CHECK_POINT="12345"
//         // (echo >&2 "[ERROR]")
//         // set
//         // rem::::: > nul == unix(>/dev/null)
//         let rbe = RegistryBroadcastEntry::new(
//             vec![
//                 "cmd".to_owned(),
//                 "/C".to_owned(),
//                 "C:\\Users\\BK-liao\\Desktop\\abc.bat".to_owned(),
//             ],
//             vec!["AYISHA_CHECK_POINT".to_owned()],
//         );

//         if let Some((stdout, stderr)) = run_cmd("abc", rbe.cmd, None, None, None, "") {
//             println!("stdout:{}", stdout);
//             println!("stderr:{}", stderr);
//         }
//     }

//     #[test]
//     fn test_bat() {
//         // C:\\Users\\BK-liao\\Desktop\\abc.bat:
//         // @echo off
//         // set AYISHA_CHECK_POINT="12345"
//         // (echo >&2 "[ERROR]")
//         // set
//         //
//         let rbe = RegistryBroadcastEntry::new(
//             vec![
//                 "cmd".to_owned(),
//                 "/C".to_owned(),
//                 "C:\\Users\\BK-liao\\Desktop\\abc.bat".to_owned(),
//             ],
//             vec!["AYISHA_CHECK_POINT".to_owned()],
//         );

//         if let Some((stdout, _stderr)) = run_cmd("abc", rbe.cmd, None, None, None, "") {
//             for mut line in stdout.lines() {
//                 if line.starts_with("export ") {
//                     let (_first, last) = line.split_at(7);
//                     line = last;
//                 }

//                 let values = line.split_once("=");
//                 if let Some(values) = values {
//                     // 保存key=value
//                     let prop = values.0.to_owned();
//                     if !rbe.props.contains(&prop) {
//                         continue;
//                     }
//                     println!(">>>>>>>{}={}", prop, values.1.to_owned());
//                 }
//             }
//         }
//     }

//     #[test]
//     fn test_mac() {
//         let rbe = RegistryBroadcastEntry::new(
//             vec![
//                 "/bin/sh".to_owned(),
//                 "-C".to_owned(),
//                 "/Users/lbk/abc.sh".to_owned(),
//             ],
//             vec!["AYISHA_CHECK_POINT".to_owned()],
//         );

//         run_cmd("abc", rbe.cmd, None, None, None, "");
//     }

//     #[test]
//     fn test_check() {
//         // let rbe = RegistryBroadcastEntry::new(
//         //     vec!["/bin/sh".to_owned(), "/Users/lbk/abc.sh".to_owned()],
//         //     vec!["AYISHA_CHECK_POINT".to_owned()],
//         // );

//         let rse = RegistryServiceEntry {
//             enable_broadcast: None,
//             env: None,
//             root: None,
//             check_interval_secs: None,
//             check_cmd: vec!["/bin/sh".to_owned(), "/Users/lbk/abc.sh".to_owned()],
//             start_cmd: vec!["/bin/sh".to_owned(), "/Users/lbk/start.sh".to_owned()],
//             to_leader_cmd: vec!["/bin/sh".to_owned(), "/Users/lbk/abc.sh".to_owned()],
//             to_follower_cmd: vec!["/bin/sh".to_owned(), "/Users/lbk/abc.sh".to_owned()],
//             broadcast: None,
//         };

//         let srv_name = "abc";
//         let common_env = None;
//         let cloned_rse = rse.clone();
//         let (_stdout, stderr) =
//             match run_cmd(&srv_name, rse.check_cmd, rse.root, common_env, rse.env, "") {
//                 Some((o, e)) => (o, e),
//                 None => return,
//             };

//         assert_ne!(stderr.len(), 0);
//         if stderr.len() == 0 {
//             return;
//         }
//         println!(
//             "Command stderr: {stderr}, running cmd: {:?}",
//             cloned_rse.start_cmd
//         );
//         if let Some((out, err)) = run_cmd(
//             &srv_name,
//             cloned_rse.start_cmd,
//             cloned_rse.root,
//             None,
//             cloned_rse.env,
//             ""
//         ) {
//             println!("Command stdout: {out}, stderr: {err}");
//         }
//     }
// }
